#include "GPUProgram.h"
#include "utils.h"
#include <iostream>
using namespace std;

GLuint CompileShader(const char * shaderPath, GLenum shaderType)
{
	GLuint shader = glCreateShader(shaderType);
	const char * code = LoadFileContent(shaderPath);
	if (!code)
	{
		cout << "can not load shader source from file " << shaderPath << endl;
		return 0;
	}
	glShaderSource(shader, 1, &code, nullptr); //ram=>vram
	glCompileShader(shader);
	GLint nResult;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &nResult);
	if (nResult == GL_FALSE)
	{
		cout << "compile shader error from file " << shaderPath << endl;
		GLint logLength;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
		char * log = new char[logLength];
		GLsizei writed = 0;
		glGetShaderInfoLog(shader, logLength, &writed, log);
		cout << log << endl;
		delete[] log;
		glDeleteShader(shader);
		return 0;
	}
	return shader;
}

GPUProgram::GPUProgram()
{
	mProgram = glCreateProgram();
}

GPUProgram::~GPUProgram()
{

}

void GPUProgram::AttachShader(GLenum shaderType, const char * shaderPath)
{
	GLuint shader = CompileShader(shaderPath, shaderType);
	do 
	{
		if (shader == 0)
		{
			break;
		}
		
		glAttachShader(mProgram, shader);
		mAttachShaders.push(shader);
	} while (0);
}


void GPUProgram::DetectAttribute(const char * attributeName)
{
	GLint loc = glGetAttribLocation(mProgram, attributeName);
	if (loc != -1)
	{
		mLocations.insert(make_pair(attributeName, loc));
	}
}

void GPUProgram::DetectUniform(const char * uniformName)
{
	GLint loc = glGetUniformLocation(mProgram, uniformName);
	if (loc != -1)
	{
		mLocations.insert(make_pair(uniformName, loc));
	}
}

GLint GPUProgram::GetLocation(const char * locationName)
{
	auto iter = mLocations.find(locationName);
	if (iter != mLocations.end())
	{
		return iter->second;
	}
	return -1;
}

void GPUProgram::Link()
{
	glLinkProgram(mProgram);
	GLint nResult;
	glGetShaderiv(mProgram, GL_COMPILE_STATUS, &nResult);
	if (nResult == GL_FALSE)
	{
		cout << "link shader error" << endl;
		GLint logLength;
		glGetShaderiv(mProgram, GL_INFO_LOG_LENGTH, &logLength);
		char * log = new char[logLength];
		GLsizei writed = 0;
		glGetShaderInfoLog(mProgram, logLength, &writed, log);
		cout << log << endl;
		delete[] log;
		glDeleteShader(mProgram);
		mProgram = 0;
	}

	while (!mAttachShaders.empty())
	{
		GLuint shader = mAttachShaders.top();
		glDetachShader(mProgram, shader);
		glDeleteShader(shader);
		mAttachShaders.pop();
	}
}
